import { GameEvent, GameEventListener, GameEventTrigger } from '@/types/game/event';
import { GameStore } from '@/types/game/index';
import { watch } from 'vue';
import eventList from './event-list';
import eventCreatorList from './event-creator';
function createGameEventLoop(gameStore: GameStore) {
    const registeredListenersMap = new Map<string, GameEventListener[]>();
    const triggerStopCollections: Array<() => void> = [];
    const gameData = gameStore.$state, gameAction = gameStore.$action;
    const eventQueue: GameEvent[] = [];
    let processing = false;
    const eventManager = { addEvent, addEventUseCreator, removeEvent, eventList, eventCreatorList }
    function start() {
        if (processing) return;
        processing = true;
        processNext();
    }
    async function processNext() {
        if (!processing) return;
        if (eventQueue.length === 0) {
            await new Promise<void>((resolve) => {
                const timer = setInterval(() => {
                    if (eventQueue.length) {
                        clearInterval(timer);
                        resolve();
                    }
                });
            })
        }
        const event = eventQueue.shift();
        console.log(event)
        if (event) {
            if (!event.actionDelay) await event.action?.(event, gameData, gameAction, eventManager);
            const listeners = registeredListenersMap.get(event.name) || [];
            for (const listener of listeners) {
                if (listener.filter === void 0 || listener.filter?.(event, gameData)) {
                    try {
                        await listener.callback(event, gameData, gameAction, eventManager);
                    } catch (err) {
                        console.error(err);
                    }
                }
            }
            if (event.actionDelay === "afterListener") {
                try {
                    await event.action(event, gameData, gameAction, eventManager);
                } catch (err) {
                    console.error(err);
                }
            }
            processNext();
        } else {
            stop();
        }
    }
    function stop() {
        processing = false;
    }
    function addEvent(eventSource: GameEvent) {
        eventQueue.push(eventSource);
    }
    function addEventUseCreator(name: string, ...args: any[]) {
        const event = eventCreatorList[name](...args);
        addEvent(event);
    }
    function addEventTrigger(trigger: GameEventTrigger) {
        const stop = watch(trigger.source, (newValue, oldValue) => {
            if (trigger.filter === void 0 || trigger.filter(newValue, oldValue, gameData)) addEvent(trigger.event);
        })
        triggerStopCollections.push(stop);
        return () => {
            const index = triggerStopCollections.indexOf(stop);
            if (index >= 0) {
                triggerStopCollections.splice(index, 1);
            }
            stop();
        };
    }
    function removeEvent(picker: GameEvent | ((event: GameEvent) => boolean)) {
        if (typeof picker === 'function') {
            for (let i = 0; i < eventQueue.length; i++) {
                if (picker(eventQueue[i])) {
                    eventQueue.splice(i--, 1);
                }
            }
        } else {
            const index = eventQueue.indexOf(picker);
            if (index >= 0) {
                eventQueue.splice(index, 1);
            }
        }
    }
    function registerEventListener(name: string, listener: GameEventListener) {
        const listenerList = registeredListenersMap.get(name);
        if (listenerList) {
            listenerList.push(listener);
            listenerList.sort((a, b) => (a.order || 0) - (b.order || 0));
        } else {
            registeredListenersMap.set(name, [listener])
        }
        return () => {
            const list = registeredListenersMap.get(name);
            if (list) {
                const index = list.indexOf(listener);
                if (index >= 0) {
                    list.splice(index, 1);
                }
            }
        }
    }
    function close() {
        eventQueue.length = 0;
        processing = false;
        registeredListenersMap.clear();
        triggerStopCollections.forEach(stop => stop());
        triggerStopCollections.length = 0;
    }
    return {
        start,
        stop,
        addEvent,
        addEventUseCreator,
        addEventTrigger,
        removeEvent,
        registerEventListener,
        close,
        get processing() {
            return processing;
        },
        get eventQueue() {
            return eventQueue;
        },
        get registeredListenersMap() {
            return registeredListenersMap;
        }
    }
}
export default {
    createGameEventLoop
}